package com.ruoyi.system.service.impl;

import cn.dev33.satoken.secure.BCrypt;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.ruoyi.common.constant.CacheNames;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.bo.SyncUserBo;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.UserBusinessType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.helper.DataBaseHelper;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.StreamUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.redis.CacheUtils;
import com.ruoyi.system.domain.SysPost;
import com.ruoyi.system.domain.SysUserPost;
import com.ruoyi.system.domain.SysUserRole;
import com.ruoyi.system.mapper.*;
import com.ruoyi.system.service.ISysUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户 业务层处理
 *
 * @author Lion Li
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class SysUserServiceImpl implements ISysUserService {

    private final SysUserMapper baseMapper;
    private final SysDeptMapper deptMapper;
    private final SysRoleMapper roleMapper;
    private final SysPostMapper postMapper;
    private final SysUserRoleMapper userRoleMapper;
    private final SysUserPostMapper userPostMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncUser(SyncUserBo bo) {
        Long businessId = bo.getBusinessId();
        SysUser sysUser = baseMapper.selectUserByBusinessId(businessId);
        if (sysUser == null) {
            sysUser = new SysUser();
            BeanUtil.copyProperties(bo, sysUser);
            // 新增：设置默认密码
            sysUser.setPassword(BCrypt.hashpw("123456"));
        }
        sysUser.setNickName(bo.getName());
        sysUser.setSex(bo.getGender());
        String mobile = bo.getMobile();
        if (StrUtil.isBlank(mobile)) {
            throw new RuntimeException("缺少手机号");
        }
        sysUser.setUserName(mobile);
        sysUser.setPhonenumber(mobile);
        if (UserConstants.NOT_UNIQUE.equals(this.checkPhoneUnique(sysUser))) {
            throw new RuntimeException("此手机号已注册过该平台");
        }
        if (UserConstants.NOT_UNIQUE.equals(this.checkUserNameUnique(sysUser))) {
            throw new RuntimeException("此手机号已注册过该平台");
        }
        if (sysUser.getUserId() == null) {
            // 注册
            // 设置未当前登录人所在的部门Id
            sysUser.setDeptId(LoginHelper.getDeptId());
            this.registerUser(sysUser);
            Long defaultRoleId = null;
            if (UserBusinessType.USER_DRUG.equals(sysUser.getBusinessType())) {
                SysRole sysRole = roleMapper.selectOne(Wrappers.<SysRole>lambdaQuery().eq(SysRole::getRoleKey, UserConstants.ROLE_DRUG));
                if (sysRole == null) {
                    log.warn("系统未初始化吸毒人员默认角色信息");
                } else {
                    defaultRoleId = sysRole.getRoleId();
                }
            } else {
                SysRole sysRole = roleMapper.selectOne(Wrappers.<SysRole>lambdaQuery().eq(SysRole::getRoleKey, UserConstants.ROLE_MEMBER));
                if (sysRole == null) {
                    log.warn("系统未初始化工作人员默认角色信息");
                } else {
                    defaultRoleId = sysRole.getRoleId();
                }
            }
            if (defaultRoleId != null) {
                this.insertUserRole(sysUser.getUserId(), new Long[]{defaultRoleId});
            }
        } else {
            // 更新
            this.updateUser(sysUser);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void bindUser(Long businessUserId, Long systemUserId) {
        if (businessUserId != null) {
            CacheUtils.evict(CacheNames.USER_MEMBER, businessUserId);
            CacheUtils.evict(CacheNames.USER_DRUG, businessUserId);
        }
        if (systemUserId != null) {
            CacheUtils.evict(CacheNames.SYS_USER_ID, systemUserId);
            SysUser sysUser = baseMapper.selectById(systemUserId);
            if(sysUser != null) {
                CacheUtils.evict(CacheNames.SYS_USER_PHONE, sysUser.getPhonenumber());
                CacheUtils.evict(CacheNames.SYS_USER_NAME, sysUser.getUserName());
                CacheUtils.evict(CacheNames.SYS_USER_OPEN_ID, sysUser.getOpenId());
            }
        }

        if (businessUserId == null) {
            if (systemUserId != null) {
                // 解绑
                SysUser sysUser = baseMapper.selectById(systemUserId);
                if (sysUser == null) {
                    throw new RuntimeException("系统用户不存在");
                }
                sysUser.setBusinessId(null);
                baseMapper.updateById(sysUser);
                clearUserCache(sysUser);
            }
        } else {
            if (systemUserId == null) {
                // 解绑
                SysUser sysUser = baseMapper.selectOne(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getBusinessId, businessUserId));
                if (sysUser != null) {
                    sysUser.setBusinessId(null);
                    baseMapper.updateById(sysUser);
                    clearUserCache(sysUser);
                }
            } else {
                // 绑定
                SysUser sysUser = baseMapper.selectById(systemUserId);
                if (sysUser == null) {
                    throw new RuntimeException("系统用户不存在");
                }
                if (sysUser.getBusinessId() != null && !Objects.equals(businessUserId, sysUser.getBusinessId())) {
                    throw new RuntimeException("该系统用户已绑定其他业务用户");
                }
                sysUser.setBusinessId(businessUserId);
                baseMapper.updateById(sysUser);
                clearUserCache(sysUser);
            }

        }
    }

    @Override
    public TableDataInfo<SysUser> selectPageUserList(SysUser user, PageQuery pageQuery) {
        Page<SysUser> page = baseMapper.selectPageUserList(pageQuery.build(), this.buildQueryWrapper(user));
        return TableDataInfo.build(page);
    }

    /**
     * 根据条件分页查询用户列表
     *
     * @param user 用户信息
     * @return 用户信息集合信息
     */
    @Override
    public List<SysUser> selectUserList(SysUser user) {
        return baseMapper.selectUserList(this.buildQueryWrapper(user));
    }

    private Wrapper<SysUser> buildQueryWrapper(SysUser user) {
        Map<String, Object> params = user.getParams();
        QueryWrapper<SysUser> wrapper = Wrappers.query();
        wrapper.eq("u.del_flag", UserConstants.USER_NORMAL).eq(user.getBusinessType() != null, "u.business_type", user.getBusinessType()).eq(user.getBusinessSubType() != null, "u.business_sub_type", user.getBusinessSubType()).eq(ObjectUtil.isNotNull(user.getUserId()), "u.user_id", user.getUserId()).like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()).eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()).like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()).between(params.get("beginTime") != null && params.get("endTime") != null, "u.create_time", params.get("beginTime"), params.get("endTime")).and(ObjectUtil.isNotNull(user.getDeptId()), w -> {
            List<SysDept> deptList = deptMapper.selectList(new LambdaQueryWrapper<SysDept>().select(SysDept::getDeptId).apply(DataBaseHelper.findInSet(user.getDeptId(), "ancestors")));
            List<Long> ids = StreamUtils.toList(deptList, SysDept::getDeptId);
            ids.add(user.getDeptId());
            w.in("u.dept_id", ids);
        });
        return wrapper;
    }

    /**
     * 根据条件分页查询已分配用户角色列表
     *
     * @param user 用户信息
     * @return 用户信息集合信息
     */
    @Override
    public TableDataInfo<SysUser> selectAllocatedList(SysUser user, PageQuery pageQuery) {
        QueryWrapper<SysUser> wrapper = Wrappers.query();
        wrapper.eq("u.del_flag", UserConstants.USER_NORMAL).eq(ObjectUtil.isNotNull(user.getRoleId()), "r.role_id", user.getRoleId()).like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()).eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()).like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber());
        Page<SysUser> page = baseMapper.selectAllocatedList(pageQuery.build(), wrapper);
        return TableDataInfo.build(page);
    }

    /**
     * 根据条件分页查询未分配用户角色列表
     *
     * @param user 用户信息
     * @return 用户信息集合信息
     */
    @Override
    public TableDataInfo<SysUser> selectUnallocatedList(SysUser user, PageQuery pageQuery) {
        List<Long> userIds = userRoleMapper.selectUserIdsByRoleId(user.getRoleId());
        QueryWrapper<SysUser> wrapper = Wrappers.query();
        wrapper.eq("u.del_flag", UserConstants.USER_NORMAL).and(w -> w.ne("r.role_id", user.getRoleId()).or().isNull("r.role_id")).notIn(CollUtil.isNotEmpty(userIds), "u.user_id", userIds).like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()).like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber());
        Page<SysUser> page = baseMapper.selectUnallocatedList(pageQuery.build(), wrapper);
        return TableDataInfo.build(page);
    }

    /**
     * 通过用户名查询用户
     *
     * @param userName 用户名
     * @return 用户对象信息
     */
    @Override
    @Cacheable(cacheNames = CacheNames.SYS_USER_NAME, key = "#userName")
    public SysUser selectUserByUserName(String userName) {
        return baseMapper.selectUserByUserName(userName);
    }

    /**
     * 通过手机号查询用户
     *
     * @param phonenumber 手机号
     * @return 用户对象信息
     */
    @Override
    @Cacheable(cacheNames = CacheNames.SYS_USER_PHONE, key = "#phonenumber")
    public SysUser selectUserByPhonenumber(String phonenumber) {
        return baseMapper.selectUserByPhonenumber(phonenumber);
    }

    /**
     * 通过用户ID查询用户
     *
     * @param userId 用户ID
     * @return 用户对象信息
     */
    @Override
    @Cacheable(cacheNames = CacheNames.SYS_USER_ID, key = "#userId")
    public SysUser selectUserById(Long userId) {
        return baseMapper.selectUserById(userId);
    }

    @Override
    @Cacheable(cacheNames = CacheNames.SYS_USER_OPEN_ID, key = "#openId")
    public SysUser selectUserByOpenId(String openId) {
        return baseMapper.selectUserByOpenId(openId);
    }

    @Override
    public List<SysUser> listUserByBusinessType(UserBusinessType businessType) {
        if (businessType == null) {
            return Lists.newArrayList();
        }
        return baseMapper.selectList(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getBusinessType, businessType));
    }

    /**
     * 查询用户所属角色组
     *
     * @param userName 用户名
     * @return 结果
     */
    @Override
    public String selectUserRoleGroup(String userName) {
        List<SysRole> list = roleMapper.selectRolesByUserName(userName);
        if (CollUtil.isEmpty(list)) {
            return StringUtils.EMPTY;
        }
        return StreamUtils.join(list, SysRole::getRoleName);
    }

    /**
     * 查询用户所属岗位组
     *
     * @param userName 用户名
     * @return 结果
     */
    @Override
    public String selectUserPostGroup(String userName) {
        List<SysPost> list = postMapper.selectPostsByUserName(userName);
        if (CollUtil.isEmpty(list)) {
            return StringUtils.EMPTY;
        }
        return StreamUtils.join(list, SysPost::getPostName);
    }

    /**
     * 校验用户名称是否唯一
     *
     * @param user 用户信息
     * @return 结果
     */
    @Override
    public String checkUserNameUnique(SysUser user) {
        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, user.getUserName()).ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId()));
        if (exist) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }

    /**
     * 校验手机号码是否唯一
     *
     * @param user 用户信息
     * @return
     */
    @Override
    public String checkPhoneUnique(SysUser user) {
        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhonenumber, user.getPhonenumber()).ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId()));
        if (exist) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }

    /**
     * 校验email是否唯一
     *
     * @param user 用户信息
     * @return
     */
    @Override
    public String checkEmailUnique(SysUser user) {
        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysUser>().eq(SysUser::getEmail, user.getEmail()).ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId()));
        if (exist) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }

    /**
     * 校验用户是否允许操作
     *
     * @param user 用户信息
     */
    @Override
    public void checkUserAllowed(SysUser user) {
        if (ObjectUtil.isNotNull(user.getUserId()) && user.isAdmin()) {
            throw new ServiceException("不允许操作超级管理员用户");
        }
    }

    /**
     * 校验用户是否有数据权限
     *
     * @param userId 用户id
     */
    @Override
    public void checkUserDataScope(Long userId) {
        if (!LoginHelper.isAdmin()) {
            SysUser user = new SysUser();
            user.setUserId(userId);
            List<SysUser> users = this.selectUserList(user);
            if (CollUtil.isEmpty(users)) {
                throw new ServiceException("没有权限访问用户数据！");
            }
        }
    }

    /**
     * 新增保存用户信息
     *
     * @param user 用户信息
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertUser(SysUser user) {
        // 新增用户信息
        int rows = baseMapper.insert(user);
        // 新增用户岗位关联
        insertUserPost(user);
        // 新增用户与角色管理
        insertUserRole(user);
        bindUser(user.getBusinessId(), user.getUserId());
        return rows;
    }

    /**
     * 注册用户信息
     *
     * @param user 用户信息
     * @return 结果
     */
    @Override
    public boolean registerUser(SysUser user) {
        user.setCreateBy(user.getUserName());
        user.setUpdateBy(user.getUserName());
        return baseMapper.insert(user) > 0;
    }

    /**
     * 修改保存用户信息
     *
     * @param user 用户信息
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateUser(SysUser user) {
        clearUserCache(user);
        Long userId = user.getUserId();
        // 删除用户与角色关联
        userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, userId));
        // 新增用户与角色管理
        insertUserRole(user);
        // 删除用户与岗位关联
        userPostMapper.delete(new LambdaQueryWrapper<SysUserPost>().eq(SysUserPost::getUserId, userId));
        // 新增用户与岗位管理
        insertUserPost(user);
        bindUser(user.getBusinessId(), user.getUserId());
        return baseMapper.updateById(user);
    }

    /**
     * 用户授权角色
     *
     * @param userId  用户ID
     * @param roleIds 角色组
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertUserAuth(Long userId, Long[] roleIds) {
        userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, userId));
        insertUserRole(userId, roleIds);
    }

    /**
     * 修改用户状态
     *
     * @param user 用户信息
     * @return 结果
     */
    @Override
    public int updateUserStatus(SysUser user) {
        clearUserCache(user);
        return baseMapper.updateById(user);
    }

    /**
     * 修改用户基本信息
     *
     * @param user 用户信息
     * @return 结果
     */
    @Override
    public int updateUserProfile(SysUser user) {
        clearUserCache(user);
        return baseMapper.updateById(user);
    }

    /**
     * 修改用户头像
     *
     * @param userName 用户名
     * @param avatar   头像地址
     * @return 结果
     */
    @Override
    public boolean updateUserAvatar(String userName, String avatar) {
        SysUser user = selectUserByUserName(userName);
        clearUserCache(user);
        return baseMapper.update(null, new LambdaUpdateWrapper<SysUser>().set(SysUser::getAvatar, avatar).eq(SysUser::getUserName, userName)) > 0;
    }

    /**
     * 重置用户密码
     *
     * @param user 用户信息
     * @return 结果
     */
    @Override
    public int resetPwd(SysUser user) {
        clearUserCache(user);
        return baseMapper.updateById(user);
    }

    /**
     * 重置用户密码
     *
     * @param userName 用户名
     * @param password 密码
     * @return 结果
     */
    @Override
    public int resetUserPwd(String userName, String password) {
        SysUser user = selectUserByUserName(userName);
        clearUserCache(user);
        return baseMapper.update(null, new LambdaUpdateWrapper<SysUser>().set(SysUser::getPassword, password).eq(SysUser::getUserName, userName));
    }

    /**
     * 新增用户角色信息
     *
     * @param user 用户对象
     */
    public void insertUserRole(SysUser user) {
        clearUserCache(user);
        this.insertUserRole(user.getUserId(), user.getRoleIds());
    }

    /**
     * 新增用户岗位信息
     *
     * @param user 用户对象
     */
    public void insertUserPost(SysUser user) {
        clearUserCache(user);
        Long[] posts = user.getPostIds();
        if (ArrayUtil.isNotEmpty(posts)) {
            // 新增用户与岗位管理
            List<SysUserPost> list = new ArrayList<>(posts.length);
            for (Long postId : posts) {
                SysUserPost up = new SysUserPost();
                up.setUserId(user.getUserId());
                up.setPostId(postId);
                list.add(up);
            }
            userPostMapper.insertBatch(list);
        }
    }

    /**
     * 新增用户角色信息
     *
     * @param userId  用户ID
     * @param roleIds 角色组
     */
    public void insertUserRole(Long userId, Long[] roleIds) {
        if (ArrayUtil.isNotEmpty(roleIds)) {
            // 新增用户与角色管理
            List<SysUserRole> list = new ArrayList<>(roleIds.length);
            for (Long roleId : roleIds) {
                SysUserRole ur = new SysUserRole();
                ur.setUserId(userId);
                ur.setRoleId(roleId);
                list.add(ur);
            }
            userRoleMapper.insertBatch(list);
        }
    }

    /**
     * 通过用户ID删除用户
     *
     * @param userId 用户ID
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteUserById(Long userId) {
        SysUser user = selectUserById(userId);
        clearUserCache(user);
        // 删除用户与角色关联
        userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, userId));
        // 删除用户与岗位表
        userPostMapper.delete(new LambdaQueryWrapper<SysUserPost>().eq(SysUserPost::getUserId, userId));
        return baseMapper.deleteById(userId);
    }

    /**
     * 批量删除用户信息
     *
     * @param userIds 需要删除的用户ID
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteUserByIds(Long[] userIds) {
        for (Long userId : userIds) {
            checkUserAllowed(new SysUser(userId));
            checkUserDataScope(userId);
        }
        List<Long> ids = Arrays.asList(userIds);
        List<SysUser> users = baseMapper.selectBatchIds(ids);
        users.forEach(SysUserServiceImpl::clearUserCache);

        // 删除用户与角色关联
        userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>().in(SysUserRole::getUserId, ids));
        // 删除用户与岗位表
        userPostMapper.delete(new LambdaQueryWrapper<SysUserPost>().in(SysUserPost::getUserId, ids));
        return baseMapper.deleteBatchIds(ids);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteUserByBusinessIds(Collection<Long> businessIds) {
        LambdaQueryWrapper<SysUser> wrapper = Wrappers.<SysUser>lambdaQuery().in(SysUser::getBusinessId, businessIds);
        List<SysUser> users = baseMapper.selectUserList(wrapper);
        users.forEach(SysUserServiceImpl::clearUserCache);
        if (CollectionUtil.isNotEmpty(users)) {
            deleteUserByIds(users.stream().map(SysUser::getUserId).collect(Collectors.toList()).toArray(new Long[]{}));
        }
    }

    @Override
    public SysUser selectUserByBusinessId(Long businessId) {
        return baseMapper.selectOne(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getBusinessId, businessId));
    }

    @Override
    public List<SysUser> listUserByBusinessIds(Collection<Long> businessIds) {
        if(CollUtil.isEmpty(businessIds)) return Lists.newArrayList();
        return baseMapper.selectList(Wrappers.<SysUser>lambdaQuery().in(SysUser::getBusinessId, businessIds));
    }

    private static void clearUserCache(SysUser user) {
        if (user == null) {
            return;
        }
        if (user.getUserId() != null) {
            CacheUtils.evict(CacheNames.SYS_USER_ID, user.getUserId());
        }
        if (StrUtil.isNotBlank(user.getUserName())) {
            CacheUtils.evict(CacheNames.SYS_USER_NAME, user.getUserName());
        }
        if (StrUtil.isNotBlank(user.getPhonenumber())) {
            CacheUtils.evict(CacheNames.SYS_USER_PHONE, user.getPhonenumber());
        }
        if (StrUtil.isNotBlank(user.getOpenId())) {
            CacheUtils.evict(CacheNames.SYS_USER_OPEN_ID, user.getOpenId());
        }
    }

}
